using Gameplay.PVE.Config;
using Gameplay.PVE.Entity;
using Gameplay.PVE.Utils;
using System.Collections.Generic;
using UnityEngine;
using Yoozoo.Libs;

namespace Gameplay.PVE.Data
{
    public enum EPveUnitTag 
    {
        hero = 1,
        summonUnit = 2,
        obstacle = 3,
        self = 4,
        parent = 5,
        machinary = 6,
    }

    public class UnitData:IClass
    {
        public struct SearchInfo
        {
            public int searchType;//0扇形  1圆形
            public Dictionary<string,float> searchArg;
        }

        public bool isDead = false;

        /// <summary>
        /// 正在播放死亡动画
        /// </summary>
        public bool isDying = false;
        
        public List<SearchInfo> searchInfos = new List<SearchInfo>();

        public int searchType;
        /*{
            get
            {
                if (PveManager.Instance.isInRpg)
                {
                    return searchTypeRpg;
                }
                else
                {
                    return searchTypePve;
                }
            }
        }*/
        
        public int battleType
        {
            get
            {
                /*if (PveManager.Instance.isInRpg)
                {
                    return config.rpgType;
                }
                else*/
                {
                    return 0;
                }
            }
        }
        public int unitType;//0战斗单位  1普通人 2车辆 3障碍 4特殊npc

        public int id;

        public int stage;

        public int team;

        public int index;

        public int autoSkillPriority;

        public int autoSkillCd;

        public int autoSkillBornCd;
        
        //本轮是否使用过技能
        public bool hasAutoUsedSkill;

        public UnitBase lockTarget;

        public BattleUnit hatredTarget;

        public float rigidRatio = 1;
        
        public float hatredDistance;

        public Vector3 position
        {
            set
            {
                m_position = value;
                if (!isGoingToHidePoint)
                {
                    astarPosition = value;
                }
            }
            get { return m_position; }
        }

        public Vector3 astarPosition;
        
        public Vector3 moveOffset;
        public Vector3 moveForwardOffset;

        private int m_moveState;

        public int moveState//-1 其他行为导致的停止 0 静止  1 走路 2 跑步
        {
            set
            {
                #if UNITY_EDITOR
                if (m_moveState != value)
                {
                    BattleLog.UnitLog(battleUnit,"moveState" + value.ToString());
                }
                #endif
                m_moveState = value;
            }
            get { return m_moveState; }
        }

        public float moveStateChangeTime;

        private int m_attackState;

        public int attackState//0 不攻击 1 站立攻击 2 移动攻击
        {
            set
            {
                BattleLog.UnitLog(battleUnit,"attackState" + value);
                m_attackState = value;
            }
            get { return m_attackState; }
        }
        
        public Vector3 manualForward
        {
            set
            {
                m_manualForward = value;
            }
            get { return m_manualForward; }
        }

        private Vector3 m_manualForward;

        public Vector3 manualAimForward;

        private Vector3 m_position;

        private Vector3 m_forward;

        public Vector3 forward
        {
            get { return m_forward; }
            set
            {
                //battleUnit?.Log("forward " + value.ToString());
                m_forward = value;
            }
        }

        public Vector3 damageForward;

        public Vector3 birthPosition;

        public Vector3 targetPosition;

        public Vector3 targetForward;

        public Vector3 patrolPosition;

        public Vector3 rendererOriginScale;
        
        public Vector3 attackPosition
        {
            set
            {
                //battleUnit?.Log(value.ToString());
                m_attackPosition = value;
            }
            get { return m_attackPosition; }
        }
        
        private Vector3 m_attackPosition;

        public Vector3 configAttackPosition;
        
        private bool isInAttack = false;

        public float attackRange = 2.5f * PveUtils.globalRatio;

        public float searchRange = 10 * PveUtils.globalRatio;

        private float m_moveSpeed = 5 * PveUtils.globalRatio;

        public float moveSpeedRatio = 1;

        public bool isSummonUnit;

        public int summonId;

        public RpgSummonConfig summonConfig;

        public int summonIndex;

        public int summonSkillIndex;
        
        public UnitBase summonParent;//召唤他的单位

        public UnitBase summonUnit;//他召唤的唯一单位
        
        public int killNum;
        
        
        private Dictionary<int,List<int>> summonUnits = new Dictionary<int,List<int>>();

        public void RemoveSummonUnit(int unitId, int summonId)
        {
            if (summonUnits.TryGetValue(summonId, out var list))
            {
                list.Remove(unitId);
            }
        }
        
        public void AddSummonUnit(int unitId,int summonId)
        {
            //先检查之前的有没有
            if (summonUnits.TryGetValue(summonId, out var list))
            {
                //检查之前有没有超标
                var config = PveUtils.GetRpgSummonConfig(summonId);
                int limit = 0;
                if (config != null)
                {
                    limit = config.exist_limit;
                }

                if (limit > 0 && limit <= list.Count)
                {
                    //超标了，找到最早加入战斗的，进行一个员的裁
                    var cutId = list[0];
                    var unit = UnitManager.Instance.GetUnitIgnoreType(cutId);
                    if (unit != null)
                    {
                        unit.Dead();
                    }
                }
            }
            else
            {
                list = new List<int>();
                summonUnits.Add(summonId,list);
            }
            list.Add(unitId);
        }
        
        /// <summary>
        /// 0 远程 1 近战
        /// </summary>
        public int rangeType;


        /// <summary>
        /// 0前排 1后排
        /// </summary>
        public int positionType;

        
        private int m_positionId;
        public int positionId
        {
            set
            {
                m_positionId = value;
            }
            get { return m_positionId; }
        }

        public bool isCanSquat;
        public float moveSpeed
        {
            get { return m_moveSpeed; }
            set
            {
                m_moveSpeed = value;
            }
        }

        public float attackSpeedRatio = 1;

        public float skillFrameSpeedChangeValue
        {
            set
            {
                m_skillFrameSpeedChangeValue = value;
                battleUnit?.Transmit(ETransmitType.SkillFrameSpeedChange);
            }
            get { return m_skillFrameSpeedChangeValue; }
        }

        private float m_skillFrameSpeedChangeValue = 1;

        public float targetMoveSpeed;

        public float moveSpeedAccelerate;

        public float configMoveSpeed = 0;

        public float walkSpeed;
        public float runSpeed;

        public float angleSpeed = 360;

        private List<UnitOrder> orderList = new List<UnitOrder>();

        public bool isDraggable = false;

        private bool isCanMove = true;

        private bool isInSkill = false;

        private bool isInMainSkill = false;

        public float damageRatio = 1;

        public bool isLogicPause = false;        
        public bool hasBattleInSight;
        public bool hasObstacleFront;
        public Vector3 battleInSightPosition;

        public Dictionary<int,HatredInfo> hatredDic = new Dictionary<int,HatredInfo>();

        public List<int> skillList = new List<int>();

        public List<int> bornElements = new List<int>();

        /// <summary>
        /// 是否被算作阻挡点
        /// </summary>
        public bool considerAstar;
        
        //掩体相关
        public RpgObstacleConfig obstacleConfig;
        
        //配表相关
        public RpgModelConfig config;

        //静态数据
        public int modelId;
        public int deadModelId;
        public int skillModelId;
        public float rpgAttackRange;
        public float pveAttackRange;
        public int rpgSkillCost;
        public int shootTurns;
        public int ammoCount;
        public List<float> shootInterval;
        public float fixedShootInterval;
        public int rpgNormalAttackId;
        public int rpgNormalSquatAttackId;
        public int rpgMoveAttackId;
        public int rpgSkillId;
        public int rpgSkillInitiativeLevel;
        public Dictionary<int,int> rpgPassiveSkillIds = new Dictionary<int, int>();

        /// <summary>
        /// 优先级,越小越高
        /// </summary>
        public Dictionary<int,int> rpgPassiveSkillPriority = new Dictionary<int, int>();
        
        public int GetPassiveSkillLevel(int id)
        {
            if (rpgPassiveSkillIds.TryGetValue(id, out var level))
            {
                return level;
            }
            return 1;
        }
        
        public int weaponType;
        public float walkSpeedRatio;
        public float runSpeedRatio;
        public int searchTypeRpg;
        public int searchTypePve;
        /// <summary>
        /// 0优先距离近  1优先距离元  2优先血量低   3优先血量高
        /// </summary>
        public int searchSort;
        public int searchChangeType;
        public float reloadTime = 2;
        public int moveType;


        public PveSkillConfig rpgNormalAttackSkillConfig;
        public PveSkillConfig rpgInitiativeSkillConfig;
        public PveSkillConfig rpgMoveAttackSkillConfig;

        public int enemyStage;
        public int enemyIndex;


        public Vector3 colliderSize;
        public Vector3 colliderCenter;

        public float skillCd;
        public float lastSkillTime;
        public float bornTime;

        public Vector3 followOffset;
        public float followOffsetDistance;
        public float followOffsetDistanceSqr;
        public UnitBase followTarget;

        public bool hasTriggerdSpecialCase;

        public bool isInSpecialCase;//是否正在特殊事件

        public List<Transform> hitPoints = new List<Transform>();

        public List<Transform> gunPoints = new List<Transform>();

        public List<Transform> bulletCasePoints = new List<Transform>();

        public List<Transform> skillEffectPoints = new List<Transform>();

        public List<Transform> buffEffectPoints = new List<Transform>();
        
        public Transform flyTextPoint;

        public Transform flyTextCriticalPoint;

        public Vector2 flyTextRange;

        public Transform hudPoint;

        public Vector3 GetHudPosition()
        {
            if (hudPoint != null)
            {
                return hudPoint.position;
            }
            return position + new Vector3(0,2,0);
        }

        public Vector3 GetHudFlyTextPosition()
        {
            return GetHudPosition() + new Vector3(0, 0.5f + hudHeight, 0);
        }
        
        public float hudHeight = 0f;

        public bool isInBirthPosition = false;

        public bool hasInitedRpg = false;

        public int leftAmmoCount = 0;

        public int totalAmmoCount = 0;

        public bool isReloading = false;

        public Dictionary<int,List<Vector3>> hidePositions = new Dictionary<int,List<Vector3>>();//墙壁用
        public UnitBase hidingUnit;//墙壁用

        public ObstacleUnit readyTohideObstacle;
        public ObstacleUnit hidingObstacle;
        public Vector3 hidePosition;
        public bool isInStageAttackPosition = false;
        public Vector3 stageAttackPosition;

        public bool isBoss;

        public bool isElite;

        public bool canShowBossWarning;
        
        public bool isShowingSpawn;


        public bool isGoingToHidePoint;
        public bool isManualMoving;
        public bool needNextShoot;
        public bool isMoveAttacking;

        public bool ignoreAmmoLimit;

        public bool isApplyAnimationMovement;

        /// <summary>
        /// 是否缴械
        /// </summary>
        public bool isDisarm;

        /// <summary>
        /// 是否沉默
        /// </summary>
        public bool isSilent;

        public bool isInAction;

        public BattleUnit battleUnit;

        public Vector3 targetHidingPoint;

        public float accuracy = 1;
        public float evade = 0;

        public Vector3 bulletForward;

        public Transform aimTransform;

        public int aimTransformIndex;

        public bool hasJoinBattle = false;

        public bool isCanMoveAttack = false;

        public int switchWeaponId;

        private float m_weaponAttackSpeed;

        public int roadType;//上中下

        public float weaponAttackSpeed
        {
            set
            {
                m_weaponAttackSpeed = value;
                attackSpeedRatio = m_weaponAttackSpeed;
                battleUnit?.Transmit(ETransmitType.AttackSpeedChange);
            }
            get { return m_weaponAttackSpeed; }
        }

        private float m_propertyAttackSpeed;

        public float propertyAttackSpeed
        {
            set
            {
                m_propertyAttackSpeed = value;
                attackSpeedRatio = m_weaponAttackSpeed * m_propertyAttackSpeed;
                battleUnit?.Transmit(ETransmitType.AttackSpeedChange);
            }
            get { return m_propertyAttackSpeed; }
        }

        public Vector3 flyTextPosition
        {
            get
            {
                var basePosition = new Vector3(0,2.18f,-0.292f);
                basePosition = Quaternion.AngleAxis(PveUtils.GetAngle(Vector3.forward, forward), Vector3.up) * basePosition;
                return basePosition + position;
            }
        }

        public Vector3 flyTextCriticalPosition
        {
            get
            {
                var basePosition = new Vector3(0,2.18f,-0.208f);
                basePosition = Quaternion.AngleAxis(PveUtils.GetAngle(Vector3.forward, forward), Vector3.up) * basePosition;
                return basePosition + position;
            }
        }

        public float lifeTime;

        public int heroId;

        public int monsterID;

        public bool hasShowBossWarning = false;

        public int GetPassivePriority(int skillId)
        {
            if (rpgPassiveSkillPriority.TryGetValue(skillId, out var p))
            {
                return p;
            }

            return 999;
        }
        
        public RpgMonsterConfig monsterConfig;

        public List<int> attackSequenceSkills = new List<int>();

        public void InitHeroConfig(int heroId)
        {
            attackSequenceSkills.Clear();
            this.heroId = heroId;
            config = PveUtils.GetRpgModelConfig(heroId);
            rangeType = config.pos_type;
            moveType = config.move_type;
            isBoss = false;
            isElite = false;
            isApplyAnimationMovement = false;
            lifeTime = 0;
            bornTime = TimeManager.logicTime;


            if (config.special_tags != null)
            {
                for (int i = 0; i < config.special_tags.Count; i++)
                {
                    AddTag(config.special_tags[i]);
                }
            }
            
            var heroData = PveUtils.GetPveHeroData(heroId);
            
            //初始化静态数据
            var battleConfig = PveUtils.GetExternalHeroConfig(heroId);
            rpgPassiveSkillIds.Clear();
            rpgPassiveSkillPriority.Clear();
            if (heroData == null)
            {
                rpgSkillId = battleConfig.rpg_skill_id;
                rpgSkillInitiativeLevel = 1;
            }
            else
            {
                attackSequenceSkills.Clear();
                rpgSkillId = heroData.skillInitiative;
                rpgSkillInitiativeLevel = heroData.skillInitiativeLevel;
                int ii = 0;
                foreach (var item in heroData.passiveSkillLevels)
                {
                    rpgPassiveSkillIds.Add(item.x,item.y);
                    if (battleConfig.rpg_passive_weights != null)
                    {
                        for (int i = 0; i < battleConfig.rpg_passive_weights.Count; i++)
                        {
                            if (battleConfig.rpg_passive_weights[i] == ii + 1)
                            {
                                rpgPassiveSkillPriority[item.x] = i;
                                break;
                            }
                        }
                    }
                    ii++;
                }
            }
            
            rpgNormalAttackId = battleConfig.rpg_normal_attack_id;
            rpgNormalSquatAttackId = battleConfig.rpg_normal_squat_attack_id;
            
            rpgMoveAttackId = battleConfig.rpg_normal_move_attack_id;
            rpgMoveAttackSkillConfig = PveUtils.GetSkillConfig(rpgMoveAttackId);
            rpgNormalAttackSkillConfig = PveUtils.GetSkillConfig(rpgNormalAttackId);
            rpgInitiativeSkillConfig = PveUtils.GetSkillConfig(rpgSkillId);
            rpgAttackRange = config.rpg_attack_range;
            pveAttackRange = config.pve_attack_range;
            modelId = config.res_id;
            searchType = config.search_type;
            searchTypePve = 0;
            searchSort = config.search_sort;
            searchChangeType = config.search_change_type;
            configMoveSpeed = config.move_speed;
            reloadTime = config.reload_time;
            totalAmmoCount = config.ammo_count;
            shootTurns = config.shoot_turns;
            ammoCount = config.ammo_count;
            rpgSkillCost = rpgInitiativeSkillConfig?.cost ?? 0;
            angleSpeed = config.angle_speed;
            walkSpeedRatio = config.walk_speed_ratio;
            runSpeedRatio = config.run_speed_ratio;
            skillModelId = config.skill_model_id;
            shootInterval = config.shoot_interval;


            ignoreAmmoLimit = false;
            weaponAttackSpeed = 1;
            propertyAttackSpeed = 1;
            walkSpeed = 1.5f;
            runSpeed = configMoveSpeed;



            accuracy = 1;
            evade = 0;

            isCanMoveAttack = rpgMoveAttackId > 0;
            Level = PveUtils.GetHeroLevel(heroId);
            Star = PveUtils.GetHeroStar(heroId);
            selectable = true;
            controlable = true;
            invulnerableList.Clear();
            blockList.Clear();

            NormalAttackDamageReduce = 0;
            NormalAttackDamageBonus = 0;
            SkillDamageReduce = 0;
            SkillDamageBonus = 0;
            
            GenderDamageBonus = new List<float> {0, 0, 0};
            GenderDamageReduce = new List<float> {0, 0, 0};
            Gender = config.gender;
            Thorns = 0;

            rigidRatio = team == 0 ? PveGlobalVlues.SelfRigidRatio : PveGlobalVlues.EnemyRigidRatio;
            InitHeroBattleProperty(PveUtils.GetHeroAttributes(heroId));
        }


        public void InitMonsterConfig(int monsterId,int level = 1)
        {
            this.monsterID = monsterId;
            var battleConfig = PveUtils.GetRpgMonsterConfig(monsterId);
            monsterConfig = battleConfig;
            config = PveUtils.GetRpgModelConfig(battleConfig.model_id);
            rangeType = config.pos_type;
            heroId = 0;
            //if (PveUtils.GetExternalHeroConfig(battleConfig.model_id) != null)
            {
                heroId = battleConfig.hero_id;
            }
            //autoSkillPriority = battleConfig.skill_priority;
            isApplyAnimationMovement = false;
            rpgAttackRange = config.rpg_attack_range;
            pveAttackRange = config.pve_attack_range;
            moveType = config.move_type;
            lifeTime = 0;
            bornTime = TimeManager.logicTime;
            bornElements.Clear();
            bornElements.AddRange(monsterConfig.born_elements);
            
            //初始化静态数据

            isBoss = battleConfig.monster_type == 3;
            isElite = battleConfig.monster_type == 2;
            canShowBossWarning = battleConfig.monster_model > 0;
            rpgSkillId = battleConfig.rpg_skill_id;
            rpgNormalAttackId = battleConfig.rpg_normal_attack_id;
            rpgNormalSquatAttackId = battleConfig.rpg_normal_squat_attack_id;
            rpgPassiveSkillIds.Clear();
            
            rpgSkillInitiativeLevel = PveUtils.GetMonsterSkillLevel(monsterId,0);
            attackSequenceSkills.Clear();
            rpgPassiveSkillPriority.Clear();
            if (battleConfig.rpg_passive_skill_id != null)
            {
                for (int i = 0; i < battleConfig.rpg_passive_skill_id.Count; i++)
                {
                    var skillLevel = PveUtils.GetMonsterSkillLevel(monsterId,i + 1);
                    if (skillLevel > 0)
                    {
                        int passiveId = battleConfig.rpg_passive_skill_id[i];
                        rpgPassiveSkillIds.Add(passiveId,skillLevel);
                        if (monsterConfig.rpg_passive_weights != null)
                        {
                            for (int j = 0; j < monsterConfig.rpg_passive_weights.Count; j++)
                            {
                                if (monsterConfig.rpg_passive_weights[j] == i + 1)
                                {
                                    rpgPassiveSkillPriority[passiveId] = j;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            rpgMoveAttackId = 0;
            rpgMoveAttackSkillConfig = null;
            rpgNormalAttackSkillConfig = PveUtils.GetSkillConfig(rpgNormalAttackId);
            rpgInitiativeSkillConfig = PveUtils.GetSkillConfig(rpgSkillId);
            rpgSkillCost = rpgInitiativeSkillConfig?.cost ?? 0;
            searchSort = config.search_sort;
            searchChangeType = config.search_change_type;
            modelId = config.res_id;
            searchType = config.search_type;
            searchTypePve = 0;
            shootTurns = config.shoot_turns;
            configMoveSpeed = config.move_speed;
            reloadTime = config.reload_time;
            totalAmmoCount = config.ammo_count;
            ammoCount = config.ammo_count;
            walkSpeedRatio = config.walk_speed_ratio;
            runSpeedRatio = config.run_speed_ratio;
            skillModelId = config.skill_model_id;
            shootInterval = config.shoot_interval;
            angleSpeed = config.angle_speed;



            ignoreAmmoLimit = false;
            weaponAttackSpeed = 1;
            propertyAttackSpeed = 1;
            walkSpeed = 1f;
            runSpeed = configMoveSpeed;



            accuracy = 1;
            evade = 0;
            Thorns = 0;
            
            this.Level = level;
            this.Star = 0;
            isCanMoveAttack = rpgMoveAttackId > 0;
            
            selectable = true;
            invulnerableList.Clear();
            controlable = true;
            blockList.Clear();
            
            NormalAttackDamageReduce = 0;
            NormalAttackDamageBonus = 0;
            SkillDamageReduce = 0;
            SkillDamageBonus = 0;

            GenderDamageBonus = new List<float> {0, 0, 0};
            GenderDamageReduce = new List<float> {0, 0, 0};
            Gender = config.gender;
            isCanMoveAttack = rpgNormalAttackSkillConfig.type == 4;
            rigidRatio = team == 0 ? PveGlobalVlues.SelfRigidRatio : PveGlobalVlues.EnemyRigidRatio;
            InitHeroBattleProperty(PveUtils.GetMonsterAttributes(monsterId,level));
        }

        public void UpdateHeroBattleProperty()
        {
            InitHeroBattleProperty(PveUtils.GetHeroAttributes(heroId));
        }

        private void InitHeroBattleProperty(List<int> attributes)
        {
            initHp = attributes[0];
            initAttack = attributes[1];
            initDefence = attributes[2];
            initDamageBonus = attributes[3] / 10000f;
            initCriticalRate = attributes[4] / 10000f;
            initCriticalBonus = attributes[5] / 10000f;
            initAttackSpeedBonus = attributes[6] / 10000f;
            initDamageReduce = attributes[7] / 10000f;
            initCriticalReduce = attributes[8] / 10000f;
            initSuckBlood = attributes[9] / 10000f;
            initHealBonus = attributes[10] / 10000f;
            /*initHpBonus = attributes[11] / 10000f;
            initAttackBonus = attributes[12] / 10000f;
            initDefenceBonus = attributes[13] / 10000f;*/

            TotalHp = initHp * (1 + initHpBonus);
            CurrentHp = TotalHp;

            mpCost = rpgInitiativeSkillConfig?.energy ?? PveGlobalVlues.MpMax;//config.job_type == 2 ? PveGlobalVlues.JobMpCost : PveGlobalVlues.MpMax;
            TotalMp = mpCost;
            mpRecoverValues.Clear();
            var attackMpBonus = config.job_type == 3 ? PveGlobalVlues.JobAttackMpBonus : 0;
            var hitMpBonus = config.job_type == 1 ? PveGlobalVlues.JobHitMpBonus : 0;
            var initMpBonus = config.job_type == 2 ? (int)TotalMp : 0;
            var allyMpBonus = config.job_type == 4 ? PveGlobalVlues.JobAllyMpBonus : 0;
            mpRecoverValues.Add(ERecoverMpType.Attack,PveGlobalVlues.AttackMpBase + attackMpBonus);
            mpRecoverValues.Add(ERecoverMpType.Hit,PveGlobalVlues.HitMpBase + hitMpBonus);
            mpRecoverValues.Add(ERecoverMpType.Kill,PveGlobalVlues.KillMpBase);
            mpRecoverValues.Add(ERecoverMpType.KillElite,PveGlobalVlues.KillEliteBase);
            mpRecoverValues.Add(ERecoverMpType.KillBoss,PveGlobalVlues.KillBossBase);
            mpRecoverValues.Add(ERecoverMpType.InitMp,PveGlobalVlues.MpInit + initMpBonus);
            mpRecoverValues.Add(ERecoverMpType.AllyAttack,allyMpBonus);

            

            
            CurrentMp = mpRecoverValues[ERecoverMpType.InitMp];
            
            AttackValue = initAttack * (1 + initAttackBonus);
            DefenceValue = initDefence * (1 + initDefenceBonus);
            CriticalRate = initCriticalRate;
            CriticalBonus = initCriticalBonus;
            propertyAttackSpeed = 1 + initAttackSpeedBonus;
            DamageBonus = initDamageBonus;
            DamageReduce = initDamageReduce;
            CriticalReduce = initCriticalReduce;
            SuckBloodRatio = initSuckBlood;
            HealBonus = initHealBonus;

            TotalShield = 0;
            CurrentShield = 0;

            float weaponRatio = 1;
            float singleAttackTime = rpgNormalAttackSkillConfig.attack_cast_time * shootTurns +
                                     rpgNormalAttackSkillConfig.storage_time + rpgNormalAttackSkillConfig.attack_recover_time;
            int bulletCount = 0;
            for (int i = 0; i < rpgNormalAttackSkillConfig.bullets.Count; i++)
            {
                var bulletConfig = PveUtils.GetSkillBulletConfig(rpgNormalAttackSkillConfig.bullets[i].y);
                if (bulletConfig != null && bulletConfig.elements != null && bulletConfig.elements.Count > 0)
                {
                    bulletCount++;
                }
            }
            int attackRounds =
                Mathf.CeilToInt(ammoCount / (float) bulletCount / shootTurns);
            float averageInterval = 0;
            if (shootInterval != null && shootInterval.Count >= 2)
            {
                averageInterval = (shootInterval[0] + shootInterval[1]) / 2f;
            }
            float totalTime = singleAttackTime * attackRounds + (attackRounds - 1) * averageInterval + reloadTime;
            normalAttackTimeEachTurn = totalTime / attackRounds;
            bulletDamageRatio = totalTime / ammoCount;
        }

        public void InitObstacleData()
        {
            GenderDamageBonus = new List<float> {0, 0, 0};
            GenderDamageReduce = new List<float> {0, 0, 0};
            rpgInitiativeSkillConfig = null;
            NormalAttackDamageReduce = 0;
            NormalAttackDamageBonus = 0;
            SkillDamageReduce = 0;
            SkillDamageBonus = 0;
            
            Gender = 2;
            accuracy = 1;
            evade = 0;
            Thorns = 0;
            
            selectable = true;
            invulnerableList.Clear();
            controlable = true;
            blockList.Clear();
        }

        public void CalculateSummonAttribute(UnitData parent,int skillLevel)
        {
            initHp = PveSummonAttributeFunction.GetAttr1(summonId, this, parent, skillLevel);
            initAttack = PveSummonAttributeFunction.GetAttr2(summonId, this, parent, skillLevel);
            initDefence = PveSummonAttributeFunction.GetAttr3(summonId, this, parent, skillLevel);
            initDamageBonus = PveSummonAttributeFunction.GetAttr4(summonId, this, parent, skillLevel);
            initCriticalRate = PveSummonAttributeFunction.GetAttr5(summonId, this, parent, skillLevel);
            initCriticalBonus = PveSummonAttributeFunction.GetAttr6(summonId, this, parent, skillLevel);
            initAttackSpeedBonus = PveSummonAttributeFunction.GetAttr7(summonId, this, parent, skillLevel);
            initDamageReduce = PveSummonAttributeFunction.GetAttr8(summonId, this, parent, skillLevel);
            initCriticalReduce = PveSummonAttributeFunction.GetAttr9(summonId, this, parent, skillLevel);
            initSuckBlood = PveSummonAttributeFunction.GetAttr10(summonId, this, parent, skillLevel);
            initHealBonus = PveSummonAttributeFunction.GetAttr11(summonId, this, parent, skillLevel);
            
            TotalHp = initHp * (1 + initHpBonus);
            CurrentHp = TotalHp;
            AttackValue = initAttack * (1 + initAttackBonus);
            DefenceValue = initDefence * (1 + initDefenceBonus);
            CriticalRate = initCriticalRate;
            CriticalBonus = initCriticalBonus;
            propertyAttackSpeed = 1 + initAttackSpeedBonus;
            DamageBonus = initDamageBonus;
            DamageReduce = initDamageReduce;
            CriticalReduce = initCriticalReduce;
            SuckBloodRatio = initSuckBlood;
            HealBonus = initHealBonus;
        }
        
        public void ClonePropertyFromParent(UnitData parent,float ratio = 0.5f)
        {
            initHp = parent.initHp * ratio;
            initAttack = parent.initAttack * ratio;
            initDefence = parent.initDefence * ratio;
            initDamageBonus = parent.initDamageReduce * ratio;
            initCriticalRate = parent.initCriticalRate * ratio;
            initCriticalBonus = parent.initCriticalBonus * ratio;
            initAttackSpeedBonus = parent.initAttackSpeedBonus * ratio;
            initDamageReduce = parent.initDamageReduce * ratio;
            initCriticalReduce = parent.initCriticalReduce * ratio;
            initSuckBlood = parent.initSuckBlood * ratio;
            initHealBonus = parent.initHealBonus * ratio;
            /*initHpBonus = attributes[11] / 10000f;
            initAttackBonus = attributes[12] / 10000f;
            initDefenceBonus = attributes[13] / 10000f;*/

            TotalHp = initHp * (1 + initHpBonus);
            CurrentHp = TotalHp;
            AttackValue = initAttack * (1 + initAttackBonus);
            DefenceValue = initDefence * (1 + initDefenceBonus);
            CriticalRate = initCriticalRate;
            CriticalBonus = initCriticalBonus;
            propertyAttackSpeed = 1 + initAttackSpeedBonus;
            DamageBonus = initDamageBonus;
            DamageReduce = initDamageReduce;
            CriticalReduce = initCriticalReduce;
            SuckBloodRatio = initSuckBlood;
            HealBonus = initHealBonus;
        }

        public int GetTargetTeam(int searchType)
        {
            if (team == 3)//障碍物
            {
                return summonParent.Data.GetTargetTeam(searchType);
            }
            
            if (searchType == 1)
            {
                return team;
            }

            return team == 2 ? 0 : 2;
        }

        //战斗实际数值

        /// <summary>
        /// 特殊buff
        /// </summary>
        public Dictionary<int,List<ElementBase>> specialBuffs = new Dictionary<int, List<ElementBase>>();
        
        /// <summary>
        /// 子弹倍率
        /// </summary>
        public float bulletDamageRatio { get; set; }

        /// <summary>
        /// 一轮普攻所需要的时长
        /// </summary>
        public float normalAttackTimeEachTurn;
        
        
        public int Level { get; set; }
        
        public int Star { get; set; }
        
        /// <summary>
        /// 实际攻击
        /// </summary>
        public float AttackValue { get; set; }

        /// <summary>
        /// 实际防御
        /// </summary>
        public float DefenceValue { get; set; }

        /// <summary>
        /// 总生命
        /// </summary>
        public float TotalHp { get; set; }

        /// <summary>
        /// 实际生命
        /// </summary>
        public float CurrentHp { get; set; }

        
        /// <summary>
        /// 能量上限
        /// </summary>
        public float TotalMp { get; set; }
        
        /// <summary>
        /// 当前能量
        /// </summary>
        public float CurrentMp { get; set; }

        public int mpCost;
        
        private Dictionary<ERecoverMpType,int> mpRecoverValues = new Dictionary<ERecoverMpType, int>();
        
        /// <summary>
        /// 总护盾
        /// </summary>
        public float TotalShield { get; set; }

        /// <summary>
        /// 实际护盾
        /// </summary>
        public float CurrentShield { get; set; }

        /// <summary>
        /// 实际伤害强化
        /// </summary>
        public float DamageBonus { get; set; }

        /// <summary>
        /// 实际暴击率
        /// </summary>
        public float CriticalRate { get; set; }

        /// <summary>
        /// 实际暴伤强化
        /// </summary>
        public float CriticalBonus { get; set; }

        /// <summary>
        /// 实际射速加成
        /// </summary>
        public float AttackSpeedBonus { get; set; }

        /// <summary>
        /// 实际伤害减免
        /// </summary>
        public float DamageReduce { get; set; }

        /// <summary>
        /// 实际暴伤减免
        /// </summary>
        public float CriticalReduce { get; set; }

        /// <summary>
        /// 实际吸血系数
        /// </summary>
        public float SuckBloodRatio { get; set; }

        /// <summary>
        /// 实际治疗强化
        /// </summary>
        public float HealBonus { get; set; }

        /// <summary>
        /// 实际生命%
        /// </summary>
        public float HpBonus { get; set; }

        /// <summary>
        /// 实际攻击%
        /// </summary>
        public float AttackBonus { get; set; }

        /// <summary>
        /// 实际防护 %
        /// </summary>
        public float DefenceBonus { get; set; }

        /// <summary>
        /// 普攻伤害减免
        /// </summary>
        public float NormalAttackDamageReduce { get; set; }

        /// <summary>
        /// 普攻伤害增加
        /// </summary>
        public float NormalAttackDamageBonus { get; set; }
        
        /// <summary>
        /// 技能伤害减免
        /// </summary>
        public float SkillDamageReduce { get; set; }

        /// <summary>
        /// 技能伤害增加
        /// </summary>
        public float SkillDamageBonus { get; set; }
        
        /// <summary>
        /// 性别增伤
        /// </summary>
        public List<float> GenderDamageBonus;
        
        /// <summary>
        /// 性别减伤
        /// </summary>
        public List<float> GenderDamageReduce;

        /// <summary>
        /// 反伤
        /// </summary>
        public float Thorns { get; set; }
        
        /// <summary>
        /// 性别
        /// </summary>
        public int Gender;

        /// <summary>
        /// 无敌
        /// </summary>
        /// <returns></returns>
        public bool invulnerable {
            get { return invulnerableList.Count > 0; }
        }

        /// <summary>
        /// 是否可被作为目标
        /// </summary>
        public bool selectable
        {
            set
            {
                m_selectable = value;
            }
            get { return m_selectable; }
        }

        private bool m_selectable;

        /// <summary>
        /// 是否可以被其他技能控制
        /// </summary>
        public bool controlable;
        
        public List<ElementBase> invulnerableList = new List<ElementBase>();

        /// <summary>
        /// 格挡列表 x概率  y减伤比率
        /// </summary>
        public List<Vector2> blockList = new List<Vector2>();

        /// <summary>
        /// 标签列表
        /// </summary>
        public Dictionary<int, int> tagList = new Dictionary<int, int>();

       
        /// <summary>
        /// 1 英雄  2 召唤物  3 掩体  4 自己  5 父子关系
        /// </summary>
        /// <param name="tagId"></param>
        /// <param name="tagValue"></param>
        public void AddTag(int tag, int tagValue = 0)
        {
            tagList[tag] = tagValue;
        }

        //英雄基础属性

        /// <summary>
        /// 基础生命
        /// </summary>
        private float initHp;

        /// <summary>
        /// 基础攻击
        /// </summary>
        public float initAttack;

        /// <summary>
        /// 基础防御
        /// </summary>
        public float initDefence;

        /// <summary>
        /// 基础能量
        /// </summary>
        public float initMp;


        //英雄特殊属性

        /// <summary>
        /// 伤害强化
        /// </summary>
        private float initDamageBonus;

        /// <summary>
        /// 暴击率
        /// </summary>
        private float initCriticalRate;

        /// <summary>
        /// 暴击倍率强化
        /// </summary>
        private float initCriticalBonus;

        /// <summary>
        /// 射速加成
        /// </summary>
        private float initAttackSpeedBonus;

        /// <summary>
        /// 伤害减免
        /// </summary>
        private float initDamageReduce;

        /// <summary>
        /// 暴击减免
        /// </summary>
        private float initCriticalReduce;

        /// <summary>
        /// 吸血系数
        /// </summary>
        private float initSuckBlood;

        /// <summary>
        /// 治疗强化
        /// </summary>
        private float initHealBonus;

        /// <summary>
        /// 生命%
        /// </summary>
        private float initHpBonus;

        /// <summary>
        /// 攻击%
        /// </summary>
        private float initAttackBonus;

        /// <summary>
        /// 防御%
        /// </summary>
        private float initDefenceBonus;


        public bool isHiding;

        //获取被代替作为目标的掩体
        public ObstacleUnit GetReplaceObstacleUnit()
        {
            if (!isHiding || hidingObstacle == null || hidingObstacle.Data.isDead)
            {
                return null;
            }

            if (hidingObstacle.Data.obstacleConfig.protect_type == 1)
            {
                return hidingObstacle;
            }
            return null;
        }
        
        public void LeaveHidePoint()
        {
            if (hidingObstacle != null && hidePosition != Vector3.zero && isHiding)
            {
                if (!hidingObstacle.Data.isDead && hidingObstacle.Data.hidePositions.TryGetValue(team, out var list))
                {
                    list.Add(hidePosition);
                    UnitManager.Instance.UnitLeaveHidePoint(hidePosition);
                }
                hidingObstacle = null;
                hidePosition = Vector3.zero;
            }
        }

        public void GoInHidePoint()
        {
            if (hidingObstacle != null)
            {
                UnitManager.Instance.UnitInHidePoint(hidePosition);
            }
        }

        public List<UnitOrder> GetOrders()
        {
            return orderList;
        }

        public void ClearOrders()
        {
            orderList.Clear();
        }

        public void RemoveOrder(UnitOrder order)
        {
            orderList.Remove(order);
        }

        public void AddOrder(UnitOrder order,bool isFront = false)
        {
            if (isFront)
            {
                orderList.Insert(0,order);
            }
            else
            {
                orderList.Add(order);
            }

        }

        public UnitOrder.EOrderType GetCurrentOrderState()
        {
            if (orderList.Count > 0)
            {
                return orderList[0].type;
            }
            return UnitOrder.EOrderType.None;
        }

        public bool HasSpecificOrder()
        {
            for (int i = 0; i < orderList.Count; i++)
            {
                if (orderList[i].type != UnitOrder.EOrderType.None && orderList[i].type != UnitOrder.EOrderType.Patrol)
                {
                    return true;
                }
            }
            return IsInSkill();
        }

        public bool IsInitiative()
        {
            return !isDead && !IsInControl();
        }

        public bool IsInStun()
        {
            return SpecialElementCount(14) > 0;
        }
        
        public bool IsInControl()
        {
            bool isStun = SpecialElementCount(14) > 0;
            bool isDrag = SpecialElementCount(11) > 0;
            bool isHit = SpecialElementCount(12) > 0;
            bool isKnock = SpecialElementCount(10) > 0;
            return isStun || isDrag || isHit || isKnock;
            //return controlType == EControlType.KnockBack || controlType == EControlType.Stun || controlType == EControlType.Drag || controlType == EControlType.Hit;
        }

        public EControlType controlType;

        public bool CanAutoUseSkill()
        {
            if (isInSkill)
            {
                return false;
            }
            if (team == 0)
            {
                bool energyEnough = rpgSkillCost <= PveManager.Instance.teamEnergy;
                bool readyToCast = PveCardManager.Instance.GetAutoCastUnitId() == id;
                if (PveGlobalVlues.UseHeroMp)
                {
                    energyEnough = CurrentMp >= mpCost;
                    readyToCast = true;
                }
                return PveManager.Instance.IsAuto && HasSkillInitiative() && !isInSkill && readyToCast && energyEnough && IsAttackInRange() && IsInScreen();
            }
            else
            {
                return hasReachAttackPosition && IsAttackInRange() && UnitManager.Instance.CheckIsCanAutoSkill(this) && IsInScreen();
            }
            return false;//PveManager.Instance.isInRpg && (team == 2 || PveManager.Instance.IsAuto) && !isInAttack && lockTarget != null && CanUseSkill() && IsSkillInRange();
        }

        public bool CanUseSkill()
        {
            /*bool isMpEnough = false;
            if (team == 0)
            {
                isMpEnough = PveManager.Instance.teamEnergy >= config.rpgSkillCost;
            }
            else
            {
                isMpEnough = true;
            }*/
            var isMpEnough = true;
            return isMpEnough && !isSilent && rpgSkillId != 0;
        }

        public bool IsSkillInCD()
        {
            var cd = team == 0 ? GetSkillCD() : GetAutoSkillCD();
            var bornCd = GetSkillBornCD();
            return cd > 0 || bornCd > 0;
        }

        public float GetSkillBornCD()
        {
            return autoSkillBornCd == 0 ? 0 : autoSkillBornCd - (TimeManager.logicTime - bornTime);
        }

        public float GetSkillCD()
        {
            if (lastSkillTime == 0 || rpgInitiativeSkillConfig == null)
            {
                lastSkillTime = 0;
                return 0;
            }
            float cd = rpgInitiativeSkillConfig.cd - (TimeManager.logicTime - lastSkillTime);
            return cd > 0 ? cd : 0;
        }

        public float GetAutoSkillCD()
        {
            if (lastSkillTime == 0 || rpgInitiativeSkillConfig == null)
            {
                lastSkillTime = 0;
                return 0;
            }

            float cd = autoSkillCd - (TimeManager.logicTime - lastSkillTime);
            return cd > 0 ? cd : 0;
        }

        public UnitBase attractAttackTarget;

        public void SetAttractAttackTarget(UnitBase target)
        {
            attractAttackTarget = target;
        }
        public void SetLockTarget(UnitBase target)
        {
            if (target != null && target.Data.team == 1)
            {
                Debug.Log("锁定了平民" + target.Data.unitType);
                return;
            }

            if (target != null && target.Data.isDead)
            {
                return;
            }

            if (target != null && target.Data.team == team && searchType == 0)
            {
                Debug.Log(id + " 锁定了队友" + " " + target.Data.id);
            }
            lockTarget = target;
        }
        
        public bool IsSkillInRange()
        {
            return IsAttackInRange();
        }

        public bool hasReachAttackPosition;

        public bool isCanAttackByFindPath = false;

        public bool isSelfMeleeAtRegion = false;

        public bool IsAttackInRange(float rangeFloat = 0)
        {
            if (PveManager.Instance.isInRpg && team == 0 && PveGlobalVlues.selfMoveType != 1 && rangeType == 0 && !isSummonUnit)//战斗时特殊处理，我方攻击距离无限大
            {
                return true;
            }

            if (rangeType == 1 && team == 0 && isSelfMeleeAtRegion)
            {
                rangeFloat = attackRange * 0.2f;
            }
            if (lockTarget == null)
            {
                return false;
            }
            if (team == 2 && !hasJoinBattle)
            {
                if (true)
                {
                    hasJoinBattle = true;
                }
                else
                {
                    return false;
                }
            }
            float distance = Vector3.Distance(position, lockTarget.Data.position);
            if (PveManager.Instance.isInRpg || team == 0)
            {
                float distance2 = 0;//Vector3.Distance(position, attackPosition);
                /*if (attackPosition != Vector3.zero)// && PveGlobalVlues.enemyUseAttackPoint && team == 2)
                {
                    distance2 = Vector3.Distance(position, attackPosition);
                    if (distance2 <= 0.01f)
                    {
                        return true;
                    }
                }*/
                //float distance3 = Vector3.Distance(attackPosition, targetPosition);
                return distance <= attackRange + rangeFloat + 0.001f && (distance2 <= 0.01f);
            }
            else if (followTarget != null)
            {
                float distance2 = Vector3.Distance(position, attackPosition);
                return distance2 <= 0.001f;
            }
            else
            {
                float distance2 = Vector3.Distance(position, attackPosition);
                return distance <= attackRange + 0.001f;// && distance2 <= 0.001f;
            }
        }

        public void RemoveLockTarget()
        {
            lockTarget = null;
        }

        public void SetIsInAttack(bool value)
        {
            isInAttack = value;
        }

        public void SetIsInSkill(bool value)
        {
            isInSkill = value;
        }

        public void SetIsCanMove(bool value)
        {
            isCanMove = value;
        }

        public void SetIsInMainSkill(bool value)
        {
            isInMainSkill = value;
        }

        public bool IsInAttack()
        {
            return isInAttack;
        }

        public bool IsInSkill()
        {
            return isInSkill;
        }

        public bool IsInMainSkill()
        {
            return isInMainSkill;
        }
        
        public bool IsCanSkill()
        {
            return !isSilent;
        }

        public bool IsCanAttack()
        {
            return !isInSkill && !isDisarm && !isInAction;
        }

        public bool IsCanMove()
        {
            return isCanMove && !isDead;
        }

        public bool HasSkillInitiative()
        {
            return rpgInitiativeSkillConfig != null;
        }

        public void SetTargetPosition(Vector3 position)
        {
            targetPosition = position;
            if (this.position == Vector3.zero)
            {
                return;
            }

            SetTargetForward(targetPosition - this.position);
        }

        public void SetTargetForward(Vector3 forward)
        {
            if (forward == Vector3.zero)
            {
                return;
            }
            forward.y = 0;
            targetForward = forward.normalized;
            //battleUnit?.Log("targetForward "+targetForward.ToString());
        }

        public int SpecialElementCount(int type)
        {
            if (specialBuffs.TryGetValue(type, out var list))
            {
                return list.Count;
            }
            return 0;
        }

        public float CheckMpChange(ERecoverMpType type,int slice = 0,float ratio = 0)
        {
            if (isDead)
            {
                return 0;
            }

            if (!HasSkillInitiative())
            {
                return 0;
            }
            if (!mpRecoverValues.TryGetValue(type, out var change))
            {
                return 0;
            }
            float value = change;
            if (type == ERecoverMpType.Attack || type == ERecoverMpType.Hit || type == ERecoverMpType.AllyAttack)
            {
                if (slice == 0)
                {
                    return 0;
                }
                value =  value * ratio;
            }
            battleUnit.SetMpChange(value);
            return value;
        }

        private bool hasInScreen = false;
        public bool firstTimeShowInScreen = false;
        
        public bool IsInScreen()
        {
            if (hasInScreen)
            {
                return true;
            }

            if (!PveCameraManager.Instance.IsInBattleCameraScreen(position))
            {
                return false;
            }
            hasInScreen = true;
            return true;
        }

        public void AddAstar()
        {
            if (!considerAstar)
            {
                PveFindPathManager.Instance.AddUnit(battleUnit);
                considerAstar = true;
            }
        }

        public void RemoveAstar()
        {
            if (considerAstar)
            {
                PveFindPathManager.Instance.RemoveUnit(battleUnit);
                considerAstar = false;
            }
        }
        
        void IClass.OnReset()
        {
            canShowBossWarning = false;
            moveOffset = Vector3.zero;
            attackSequenceSkills.Clear();
            killNum = 0;
            bornElements.Clear();
            firstTimeShowInScreen = false;
            isDying = false;
            attractAttackTarget = null;
            summonUnits.Clear();
            RemoveAstar();
            mpRecoverValues.Clear();
            hidingObstacle = null;
            rendererOriginScale = Vector3.one;
            isSelfMeleeAtRegion = false;
            rigidRatio = 1;
            isCanAttackByFindPath = false;
            autoSkillBornCd = 0;
            hasShowBossWarning = false;
            considerAstar = false;
            rpgPassiveSkillIds.Clear();
            tagList.Clear();
            hasInScreen = false;
            hasAutoUsedSkill = false;
            positionType = 0;
            hasReachAttackPosition = false;
            summonConfig = null;
            isInMainSkill = false;
            selectable = true;
            isMoveAttacking = false;
            specialBuffs.Clear();
            team = 0;
            hatredTarget = null;
            isSummonUnit = false;
            hasJoinBattle = false;
            isHiding = false;
            isDead = true;
            lockTarget = null;
            isCanMove = true;
            isInAttack = false;
            isInSkill = false;
            orderList.Clear();
            isDraggable = false;
            hatredDic.Clear();
            searchInfos.Clear();
            skillList.Clear();
            followTarget = null;
            attackPosition = Vector3.zero;
            isInSpecialCase = false;
            hasInitedRpg = false;
            hidingUnit = null;
            isInStageAttackPosition = false;
            isShowingSpawn = false;
            deadModelId = 0;
            isInAction = false;
            isReloading = false;
            position = Vector3.zero;
            isManualMoving = false;
            manualForward = Vector3.zero;
            hudPoint = null;
            hudHeight = 0;
            isGoingToHidePoint = false;
            gunPoints = new List<Transform>();
            isLogicPause = false;
            battleUnit = null;
            skillModelId = 0;
            moveState = 0;
            summonParent = null;
            attackState = 0;
            rpgInitiativeSkillConfig = null;
            obstacleConfig = null;
        }
    }

    public class HatredInfo
    {
        public int id;
        public BattleUnit source;
        public float hatresTime;
    }

}
